﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Threading.Tasks;
using Org.LyrcsoftFramework.Cloud.Core;

namespace Org.LyrcsoftFramework.Cloud.Feign
{
    public interface IFeignProxyBuilder : IProxyBuilder
    {

        public class FeignProxyBuilder : IFeignProxyBuilder
        {
            public static Dictionary<string, MethodInfo> MethodsCache { get; set; } = new Dictionary<string, MethodInfo>();

            private Type targetType;

            private static ConcurrentDictionary<string, Type> TargetTypeCache { set; get; } =
                new ConcurrentDictionary<string, Type>();


            public Object Build(Type interfaceType, params Object[] constructor)
            {
                String cacheKey = interfaceType.FullName;
                TargetTypeCache.TryGetValue(cacheKey, out var resultType);
                Object result = Activator.CreateInstance(resultType, args: constructor);
                return result;
            }

            /// <summary>
            /// 实现接口
            /// </summary>
            /// <param name="interfaceType"></param>
            public void InitInterface(Type interfaceType)
            {
                String cacheKey = interfaceType.FullName;
                Type resultType = BuildTargetType(interfaceType);
                TargetTypeCache.TryAdd(cacheKey, resultType);
            }

            /// <summary>
            /// 动态生成接口的实现类（这是只是定义），
            /// 生成一个构造函数，该构造函数将 httpService 参数赋值给类的 httpService 字段，
            /// 并创建了一个新的 List<Object> 对象并赋值给类的 paramterArrField 字段，最后返回构造函数。
            /// 这样，当类实例化时，构造函数会自动执行这些操作。
            /// </summary>
            /// <param name="interfaceType">申明需要实现的接口</param>
            /// <returns></returns>
            private Type BuildTargetType(Type interfaceType)
            {
                targetType = interfaceType;
                // 定义动态程序集、模块和类型的名称
                string assemblyName = targetType.Name + "ProxyAssembly";
                string moduleName = targetType.Name + "ProxyModule";
                string typeName = targetType.Name + "Proxy";
                // 定义程序集名字
                AssemblyName assyName = new AssemblyName(assemblyName);
                // 创建动态程序集
                AssemblyBuilder assyBuilder = AssemblyBuilder.DefineDynamicAssembly(assyName, AssemblyBuilderAccess.Run);
                // 创建动态模块
                ModuleBuilder modBuilder = assyBuilder.DefineDynamicModule(moduleName);
                // 定义新类型的属性，类公开
                TypeAttributes newTypeAttribute = TypeAttributes.Class | TypeAttributes.Public;
                //父类型
                Type parentType;
                //要实现的接口
                Type[] interfaceTypes;

                if (targetType.IsInterface)
                {
                    parentType = typeof(object);
                    // 如果传入的是接口类型，则作为要实现的唯一接口
                    interfaceTypes = new Type[] { targetType };
                }
                else
                {
                    parentType = targetType;
                    // 如果传入的是普通类型，则没有要实现的接口
                    interfaceTypes = Type.EmptyTypes;
                }
                //得到类型生成器            
                TypeBuilder typeBuilder = modBuilder.DefineType(typeName, newTypeAttribute, parentType, interfaceTypes);

                //定义一个字段存放 HttpService 实例
                var httpType = typeof(HttpService);
                FieldBuilder httpServiceField = typeBuilder.DefineField("httpService", httpType, FieldAttributes.Public);

                //定义一个集合存放参数集合
                FieldBuilder paramterArrField = typeBuilder.DefineField("paramterArr", typeof(List<object>), FieldAttributes.Public);
                //创建构造函数
                ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { httpType });

                //il创建构造函数，对httpService字段进行赋值，同时初始化存放参数的集合
                ILGenerator ilgCtor = constructorBuilder.GetILGenerator();
                // Ldarg_0 是一个 IL 指令，它将第一个参数（即构造函数的隐式参数，指向当前实例）加载到堆栈上。
                ilgCtor.Emit(OpCodes.Ldarg_0);
                // Ldarg_1 将第二个参数（在这里是 httpService）加载到堆栈上。
                ilgCtor.Emit(OpCodes.Ldarg_1);
                //Stfld 将堆栈上的值存储到字段 httpServiceField 中。这行代码的作用是将第二个参数 httpService 赋值给类的字段 httpService。
                ilgCtor.Emit(OpCodes.Stfld, httpServiceField);

                ilgCtor.Emit(OpCodes.Ldarg_0); //加载当前类
                //Newobj 是一个 IL 指令，它会创建一个新的对象实例。这里创建了一个 List<Object> 对象。
                ilgCtor.Emit(OpCodes.Newobj, typeof(List<Object>).GetConstructors().First());
                //将新创建的 List<Object> 对象存储到字段 paramterArrField 中。
                ilgCtor.Emit(OpCodes.Stfld, paramterArrField);
                //Ret 是一个 IL 指令，它表示方法返回。这里表示构造函数执行结束，返回。
                ilgCtor.Emit(OpCodes.Ret); //返回
                // 获取接口的所有方法
                MethodInfo[] targetMethods = targetType.GetMethods();

                foreach (MethodInfo targetMethod in targetMethods)
                {
                    //只挑出virtual的方法
                    if (targetMethod.IsVirtual)
                    {
                        //缓存接口的方法体，便于后续将方法体传递给httpService
                        string methodKey = Guid.NewGuid().ToString();
                        MethodsCache[methodKey] = targetMethod;

                        //得到方法的各个参数的类型和参数
                        var paramInfo = targetMethod.GetParameters();
                        var parameterType = paramInfo.Select(it => it.ParameterType).ToArray();
                        var returnType = targetMethod.ReturnType;
                        //方法返回值只能是task，即只支持异步，因为http操作是io操作
                        if (!typeof(Task).IsAssignableFrom(returnType)) throw new Exception("return type must be task<>");
                        var underType = returnType.IsGenericType ? returnType.GetGenericArguments().First() : returnType;

                        //通过emit生成方法体
                        MethodBuilder methodBuilder = typeBuilder.DefineMethod(targetMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, targetMethod.ReturnType, parameterType);
                        ILGenerator ilGen = methodBuilder.GetILGenerator();

                        MethodInfo executeMethod = null;
                        if (returnType == typeof(Task))
                        {
                            var methodTmp = httpType.GetMethod("ExecuteNoReturnAsync");
                            if (methodTmp == null) throw new Exception("找不到执行方法");
                            executeMethod = methodTmp;
                        }
                        else
                        {
                            var methodTmp = httpType.GetMethod("ExecuteAsync");

                            if (methodTmp == null) throw new Exception("找不到执行方法");
                            executeMethod = methodTmp.IsGenericMethod ? methodTmp.MakeGenericMethod(underType) : methodTmp;
                        }


                        // 栈底放这玩意，加载字段前要加载类实例，即Ldarg_0
                        ilGen.Emit(OpCodes.Ldarg_0);
                        ilGen.Emit(OpCodes.Ldfld, httpServiceField);

                        //把所有参数都放到list<object>里
                        ilGen.Emit(OpCodes.Ldarg_0);
                        ilGen.Emit(OpCodes.Ldfld, paramterArrField);
                        for (int i = 0; i < parameterType.Length; i++)
                        {
                            ilGen.Emit(OpCodes.Dup);
                            ilGen.Emit(OpCodes.Ldarg_S, i + 1);
                            if (parameterType[i].IsValueType)
                            {
                                ilGen.Emit(OpCodes.Box, parameterType[i]);
                            }
                            ilGen.Emit(OpCodes.Callvirt, typeof(List<Object>).GetMethod("Add"));
                        }

                        // 当前栈[httpServiceField paramterArrField]
                        //从缓存里取出方法体
                        ilGen.Emit(OpCodes.Call, typeof(FeignProxyBuilder).GetMethod("get_MethodsCache", BindingFlags.Static | BindingFlags.Public));
                        ilGen.Emit(OpCodes.Ldstr, methodKey);
                        ilGen.Emit(OpCodes.Call, typeof(Dictionary<string, MethodInfo>).GetMethod("get_Item"));

                        ilGen.Emit(OpCodes.Callvirt, executeMethod);
                        //清空list里的参数
                        ilGen.Emit(OpCodes.Ldarg_0);
                        ilGen.Emit(OpCodes.Ldfld, paramterArrField);
                        ilGen.Emit(OpCodes.Callvirt, typeof(List<Object>).GetMethod("Clear"));
                        // pop the stack if return void
                        if (targetMethod.ReturnType == typeof(void))
                        {
                            ilGen.Emit(OpCodes.Pop);
                        }
                        // complete
                        ilGen.Emit(OpCodes.Ret);
                        typeBuilder.DefineMethodOverride(methodBuilder, targetMethod);
                    }
                }

                var resultType = typeBuilder.CreateTypeInfo().AsType();
                return resultType;
            }

            public T Build<T>(params Object[] constructor)
            {
                return (T)this.Build(typeof(T), constructor);
            }
        }
    }
}
